#include <cstdlib>
#include <iostream>
#include <list>
#include <vector>
#include <string>
#include <ctime>

#include "Graph.h"
#include "Node.h"

/* ----------------------------------------------------- */

Graph::Graph() {
    vector<NodeList *> * lnodelist = new vector<NodeList *>();
    adjacency_list = lnodelist;
}

Graph::Graph(int n, double p) {
    vector<NodeList *> * lnodelist = new vector<NodeList *>();
    srand(time(NULL));

    for (int k = 0; k < n; k++) {
        NodeList *nl = new NodeList();
        Node *n = new Node(k, -1);
        nl->push_back(n);
        lnodelist->push_back(nl);
    }

    double x;
    for (int i = 0; i < n - 1; i++) {
        for (int j = i + 1; j < n; j++) {
            x = (double) (rand() / (double) RAND_MAX);
            if (x < p) {
                NodeList *nl_i;
                NodeList *nl_j;
                for (lnodelist_it lnl = lnodelist->begin(); lnl != lnodelist->end(); lnl++) {
                    Node *front_node = (*lnl)->front();

                    if (front_node->getValue() == j) {
                        nl_j = *lnl;
                    }
                    if (front_node->getValue() == i) {
                        nl_i = *lnl;
                    }
                }
                nl_i->push_back(nl_j->front());
                nl_j->push_back(nl_i->front());
            }
        }
    }
    adjacency_list = lnodelist;
}

Graph::Graph(string graph_file_path) {
    ifstream graph_file(graph_file_path.c_str());


    //Compte le nombre de sommets en comptant le nombre de lignes non vides
    string s;
    string vide("");
    unsigned int n = 0;
    if (graph_file)
        while (getline(graph_file, s)) {
            if (s.compare(vide) != 0)
                ++n;
        }

    //On creer les N nodelist (correspondant a chaque sommet)
    vector<NodeList *> *lnodelist = new vector<NodeList *>();
    for (int i = 0; i < n; i++) {
        NodeList *nl = new NodeList();
        Node *n = new Node(i, -1);
        nl->push_back(n);
        lnodelist->push_back(nl);
    }

    ifstream graph_file2(graph_file_path.c_str());

    int current_i;
    while (graph_file2 >> s) {
        if (s.at(s.size() - 1) == ':')
            current_i = atoi(s.substr(0, s.size() - 1).c_str());
        else {
            int value = atoi(s.c_str());
            NodeList * nodelist = (*lnodelist)[current_i];
            NodeList * ntruc = (*lnodelist)[value];
            nodelist->push_back(ntruc->front());
        }
    }


    adjacency_list = lnodelist;

    graph_file.close();
    graph_file2.close();
}

Graph::Graph(int n, double p, int k_color) {
    srand(time(NULL));
    double x;

    vector<NodeList *> * lnodelist = new vector<NodeList *>();
    NodeList * nodelist = new NodeList();

    for (int i = 0; i < n; i++) {
        NodeList * nl = new NodeList();
        x = (double) (rand() / (double) RAND_MAX);
        int color = (int) (x * k_color);
        Node * n = new Node(i, color);
        nl->push_back(n);
        nodelist->push_back(n);
        lnodelist->push_back(nl);
    }

    for (int i = 0; i < n - 1; i++) {
        for (int j = i + 1; j < n; j++) {
            x = (double) (rand() / (double) RAND_MAX);
            if (x < p) {
                NodeList *nl_i;
                NodeList *nl_j;
                for (lnodelist_it y = lnodelist->begin(); y != lnodelist->end(); y++) {
                    Node *front_node = (*y)->front();
                    if (front_node->getValue() == j)
                        nl_j = *y;
                    if (front_node->getValue() == i)
                        nl_i = *y;
                }
                if (nl_i->front()->getColor() != nl_j->front()->getColor()) {
                    nl_i->push_back(nl_j->front());
                    nl_j->push_back(nl_i->front());
                }
            }
        }
    }
    adjacency_list = lnodelist;
    for (int i = 0; i < adjacency_list->size(); ++i) {
        //cout << i <<endl;
        NodeList * nl = (*adjacency_list)[i];
        Node * n = nl->front();
        n->setColor(-1);
    }
}

Graph::~Graph() {
    for (lnodelist_it i = adjacency_list->begin(); i != adjacency_list->end(); i++) {
        for (nodelist_it j = (*i)->begin(); j != (*i)->end(); j++) {
            //Node * n = *j;
            //delete n;
        }
        delete *i;
    }
    delete adjacency_list;
}

/* ----------------------------------------------------- */


Graph * Graph::clone() {

    Graph * copie = new Graph();

    //On creer les N nodelist (correspondant a chaque sommet)
    vector<NodeList *> *lnodelist = new vector<NodeList *>();

    int n = adjacency_list->size();
    for (int i = 0; i < n; i++) {
        NodeList *nl = new NodeList();
        Node *n = new Node(i, -1);
        nl->push_back(n);
        lnodelist->push_back(nl);
    }

    for (lnodelist_it i = adjacency_list->begin(); i != adjacency_list->end(); ++i) {
        for (nodelist_it j = ++((*i)->begin()); j != (*i)->end(); ++j) {
            int value = (*i)->front()->getValue();
            NodeList * nodelist = (*lnodelist)[value];
            NodeList * ntruc = (*lnodelist)[(*j)->getValue()];
            nodelist->push_back(ntruc->front());
        }
    }

    copie->adjacency_list = lnodelist;
    return copie;
}

int Graph::size() {
    return adjacency_list->size();
}

int Graph::vertice_nb(){
  int size = 0;
  for (lnodelist_it i = adjacency_list->begin(); i != adjacency_list->end(); ++i) {
    size += ((*i)->size()) - 1;
  }

  return size;
}

/* ----------------------------------------------------- */
vector<NodeList *> * Graph::getAdjacencyList() {
    return adjacency_list;
}

ostream & operator <<(std::ostream& out, const Graph & g) {
    for (lnodelist_it i = g.adjacency_list->begin(); i != g.adjacency_list->end(); ++i) {
        for (nodelist_it j = (*i)->begin(); j != (*i)->end(); ++j) {
            out << **j;
        }
        out << endl;
    }
    return out;
}

string colorToString(Color c) {
    switch (c) {
        case 0:
            return "red";
            break;
        case 1:
            return "blue";
            break;
        case 2:
            return "yellow";
            break;
        case 3:
            return "green";
            break;
        case 4:
            return "brown";
            break;
        case 5:
            return "orange";
            break;
        case 6:
            return "grey";
            break;
        case 7:
            return "pink";
            break;
        default:
            return "white";
    }
}

void Graph::saveOnDotFile(string dot_file_path) {
    ofstream dot_file(dot_file_path.c_str());

    dot_file << "strict graph G {" << endl;

    for (lnodelist_it i = adjacency_list->begin(); i != adjacency_list->end(); i++) {
        Node * front_node = (*i)->front();
        dot_file << "\tnode [style=filled fillcolor=\"";
        dot_file << colorToString(front_node->getColor()) << "\" ]";
        dot_file << front_node->getValue() << ";" << endl;
    }

    for (lnodelist_it i = adjacency_list->begin(); i != adjacency_list->end(); i++) {
        nodelist_it j = (*i)->begin();
        int front_value = (*j)->getValue();
        j++;
        for (; j != (*i)->end(); j++) {
            dot_file << "\t" << front_value << " -- " << (*j)->getValue() << ";" << endl;
        }
    }

    dot_file << "}" << endl;

}

void Graph::saveOnFile(string graph_file_path) {
    ofstream graph_file(graph_file_path.c_str());
    for (lnodelist_it i = adjacency_list->begin(); i != adjacency_list->end(); i++) {
        nodelist_it j = (*i)->begin();
        graph_file << (**j).getValue() << ":";
        j++;
        for (; j != (*i)->end(); j++) {
            graph_file << " " << (**j).getValue();
        }
        graph_file << endl;
    }

}

void Graph::saveOnCnfFile(string cnf_file_path, int k) {
    ofstream cnf_file(cnf_file_path.c_str());
    int n = this->size();
    int indice = 0;
    cnf_file << "p cnf " << n * k << " " << n * (k + 1) + k * this->vertice_nb() << endl;

    for (int i = 1; i <= n; i++) {
        int color;
        for (color = 1; color <= k; color++)
            cnf_file << indice + color << " ";
        cnf_file << 0 << endl;

        int tmp = k;
        for (color = 1; color <= k; color++) {
            for (int l = 1; l <= k; l++) {
                if (tmp == l)
                    continue;
                cnf_file << -(indice + l) << " ";
            }
            cnf_file << 0 << endl;
            tmp--;
        }

        for (nodelist_it voisin = ++((*adjacency_list)[i-1]->begin()); voisin != (*adjacency_list)[i-1]->end(); ++voisin) {
	  for (color = 1; color <= k; color++)
	    cnf_file << -(indice + color) << " " << -(k * (*voisin)->getValue() + color) << " " << 0 << endl;
	}
        indice += k;
    }
}
